# Title: 事件工单稽核
# Params: month|one_str|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/8/8 下午3:46
# @Author: wen
# @Email:happiness.sq@foxmail.com
# @README:
import concurrent.futures
import datetime
import sys
import warnings
import socket
import requests
import json
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from base64 import b64encode
from urllib3.exceptions import InsecureRequestWarning

warnings.filterwarnings("ignore", message="Unverified HTTPS request", category=InsecureRequestWarning)


def is_port_open(host: str, port: int) -> bool:
    """检查指定的主机和端口是否开放"""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.settimeout(3)  # 设置超时时间为 3 秒
        try:
            sock.connect((host, port))
            return True
        except (socket.timeout, socket.error):
            return False


def zero_padding(data: bytes, block_size: int) -> bytes:
    """手动实现零填充"""
    padding_length = block_size - (len(data) % block_size)
    padding = bytes([0] * padding_length)  # 使用 0x00 填充
    return data + padding


def base_encrypt(plaintext: bytes) -> str:
    key = b'3424598329482949'
    iv = b'3424598329482949'

    padded_data = zero_padding(plaintext, 16)
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(padded_data) + encryptor.finalize()

    encoded_ciphertext = b64encode(ciphertext).decode('utf-8')
    return encoded_ciphertext


class yun_ge:
    def __init__(self, log):
        """替换为自己的cookies,即可使用"""
        self.json_data = {}
        self.taskId = None
        self.headers = {
            "accept": "*/*",
            "accept-language": "zh-CN,zh;q=0.9",
            "authorization": "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ6aGFvd2VuY2hlbmciLCJleHAiOjE3MjMzNjIzNDEsImlhdCI6MTcyMzEwMzE0MX0.CgfeGitP2nPIigGHWS7tzZknzY5tg0BZ9rERa8SbGozFc2v_AZns2WpJU9jZ0eeJkT0hB9MRbfi3gfDSlkVqdA",
            "cache-control": "no-cache",
            "content-type": "application/json;charset=UTF-8",
            "pragma": "no-cache",
            "sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
        }
        self.log = log
        self.cookies = {
            "sso_t_name": "uniToken",
            "sso_t_value": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJpcFwiOlwiMTk3LjE2Ni4yMzQuMTI4XCIsXCJuYW1lXCI6XCLotbXmloflrqxcIixcIm1vYmlsZVwiOlwiMTg4ODQ5MDExMjVcIixcImFjY291bnRcIjpcInpoYW93ZW5jaGVuZ1wiLFwic3ViQWNjb3VudFwiOlwiemhhb3dlbmNoZW5nXCJ9IiwibmJmIjoxNzIzMTAyOTg3LCJpc3MiOiJwb3J0YWwtc3Rld2FyZCIsImV4cCI6MTcyMzEwMzA0NywiaWF0IjoxNzIzMTAyOTg3LCJqdGkiOiI1MDliNDVmOWQ3NWQ0ZjQzYTBiOGNhZmJhOGNjYTY3OSJ9.0qZzFl3Fc-XXocWwW_DMlEhmkmbeTl0MDzQTp9rFYR8"
        }
        self.urls = {
            '172.17.42.253': 28080,  # sdp_url
            '172.21.152.199': 10372,  # zw_url
            '10.217.241.40': 10372
        }
        self.url = self.find_open_url()
        if not self.url:
            self.log('网络连接失败，无法继续执行。请检查网络')
            return
        today = datetime.datetime.now()
        self.date = today.strftime('%Y-%m-%d %H:%M:%S')
        self.next_day = (today + datetime.timedelta(days=20)).strftime('%Y-%m-%d %H:%M:%S')
        url = f"https://{self.url}/uc/sso/auth"
        params = {
            "code": "SHA3RTNqSW95WGlOd0lNQXlSeEFGQT09",
            "service": "s",
            "type": "umsToken"
        }
        self.session = requests.Session()
        self.session.cookies.update(self.cookies)
        response = self.session.get(url, headers=self.headers, params=params, verify=False, timeout=10)
        if response.json()['token']:
            self.headers['authorization'] = "Bearer " + response.json()['token']
            self.token = "Bearer " + response.json()['token']
            self.log("获取token成功")
        else:
            self.log("获取token失败")

    def find_open_url(self):
        """根据端口开放情况选择 URL"""
        for host, port in self.urls.items():
            if is_port_open(host, port):
                return f'{host}:{port}'
        return None

    def work_list(self):
        # 事件跟踪单完结稽核
        url = f"https://{self.url}/runtime/runtime/instance/statistic/v1/list"
        data = {
            "proc_def_key": "xtsjgzlc",
            "resource_pool": "贵阳资源池",
            "start_date": "",
            "end_date": "",
            "origin_person": [],
            "solve_person": [],
            "org_id": "",
            "query_key": "",
            "subject": "",
            "recordId": "",
            "instanceName": "",
            "page_size": 100,
            "page_no": 1,
            "status": "end",
            "department1": "",
            "department2": ""
        }
        data = json.dumps(data, separators=(',', ':'))
        response = self.session.post(url, headers=self.headers, data=data, verify=False)
        result = response.json()["result"]
        return result

    def work_running(self):
        # 事件跟踪单运行中稽核
        now = datetime.datetime.now()
        first_day_of_month = now.replace(day=1)
        first_day_of_month_str = first_day_of_month.strftime('%Y-%m-%d')
        data = {
            "proc_def_key": "",
            "resource_pool": "贵阳资源池",
            "start_date": first_day_of_month_str,
            "end_date": now.strftime('%Y-%m-%d'),
            "origin_person": [],
            "solve_person": [],
            "org_id": "",
            "query_key": "",
            "subject": "",
            "recordId": "",
            "instanceName": "",
            "page_size": 100,
            "page_no": 1,
            "status": "running",
            "department1": "",
            "department2": ""
        }
        data = json.dumps(data, separators=(',', ':'))
        url = f"https://{self.url}/runtime/runtime/instance/statistic/v1/list"
        response = self.session.post(url, headers=self.headers, data=data, verify=False)
        result = response.json()["result"]
        filter_result = [i for i in result if i.get("curNodeName") == "一线运维处理"]
        return filter_result

    def work_close_time(self, instId):
        url = f"https://{self.url}/runtime/runtime/instance/v1/instanceFlowOpinions"
        payload = "{\"instId\":\"%s\",\"isPc\":true}" % base_encrypt(instId.encode("utf-8"))
        headers = {
            "accept": "*/*",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "authorization": self.token,
            "bpm-nonceid": "tmEIecu54A8225897717375957730289995601",
            "cache-control": "no-cache",
            "content-type": "application/json;charset=UTF-8",
            "origin": "https",
            "pragma": "no-cache",
            "priority": "u=1, i",
            "referer": "https",
            "sec-ch-ua": "\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0"
        }

        response = self.session.post(url, data=payload, headers=headers, verify=False)
        for i in response.json():
            statusVal = i.get("statusVal")
            create_time_str = i.get("createTime")
            create_time = datetime.datetime.strptime(create_time_str, "%Y-%m-%d %H:%M:%S")
            if statusVal == '待审批':
                now = datetime.datetime.now()
                strftime = now.strftime('%Y-%m-%d %H:%M:%S')
                strf_time = datetime.datetime.strptime(strftime, "%Y-%m-%d %H:%M:%S")
                time_diff = strf_time - create_time
                if time_diff.days < 5:
                    return True
                else:
                    print(instId, f"工单超过5未关闭,请检查该工单")
                    return False

            if i.get("taskName") == "一线运维处理":
                complete_time_str = i.get("completeTime")
                # 将时间字符串解析为 datetime 对象
                complete_time = datetime.datetime.strptime(complete_time_str, "%Y-%m-%d %H:%M:%S")

                # 计算时间差
                time_difference = complete_time - create_time
                opinion = i.get("opinion")
                statusVal = i.get("statusVal")
                if opinion and statusVal != "同意":
                    print(f"{instId} 工单审批未选择关键词'同意',请检查该工单")
                # 判断时间差是否小于7天
                if time_difference.days < 7:
                    return True

                else:
                    print(instId, f"工单关闭时间大于7天,请检查该工单")
                    return False

    def work_info(self, proinstid):
        url = f"https://{self.url}/runtime/runtime/instance/v1/getInstFormAndBONew"
        params = {
            "identification": "data",
            "proInstId": proinstid,
            "nodeId": ""
        }

        # 发起 GET 请求
        response = self.session.get(url, headers=self.headers, params=params, verify=False, timeout=10)
        data = response.json().get("data", {})
        xtsjgzdx = data.get("xtsjgzdx", {})

        res = xtsjgzdx.get("ejbm")  # 基础平台部
        yjbm = xtsjgzdx.get("yjbm")  # 中移信息
        ipdz = xtsjgzdx.get("ipdz")  # 管理ip
        sjwtly = xtsjgzdx.get("sjwtly")  # 运维发现 or 告警同步
        glgdbh = xtsjgzdx.get("glgdbh")  # 关联工单编号
        sjxz = xtsjgzdx.get("sjxz")  # 普通事件

        sjclgcjjg = xtsjgzdx.get("sjclgcjjg")  # 事件处理过程及结果

        # 校验字段值
        err = f'稽核存在问题工单号：{proinstid}  '

        if sjxz != '普通事件':
            return err
        if res != '基础平台部':
            self.log(proinstid, '二级部门未填写 基础平台部')
            return err
        if yjbm != '中移信息':
            self.log(proinstid, '一级部门未填写成  中移信息')
            return err
        if not ipdz:
            self.log(proinstid, '资产编号的管理ip未填写')
            return err
        if sjwtly == '运维发现' and glgdbh:
            self.log(proinstid, '当前事件问题来源：运维发现，却填写告警工单号')
            return err
        if sjwtly == '告警同步' and not glgdbh:
            self.log(proinstid, '当前事件问题来源：告警同步，未填写告警工单号')
            return err
        if sjwtly not in ["告警同步", "运维发现"]:
            self.log(proinstid, '当前事件问题来源：字段填写错误，请检查')
            return err

        sjdjsj = xtsjgzdx.get("sjdjsj")  # 事件登记时间
        sjcssj = xtsjgzdx.get("sjcssj")  # 事件产生时间
        sjjjsj = xtsjgzdx.get("sjjjsj")  # 事件解决时间
        # 解析时间
        try:
            sjdjsj_time = datetime.datetime.strptime(sjdjsj, '%Y-%m-%d %H:%M:%S')
            sjcssj_time = datetime.datetime.strptime(sjcssj, '%Y-%m-%d %H:%M:%S')
            sjjjsj_time = datetime.datetime.strptime(sjjjsj, '%Y-%m-%d %H:%M:%S')
        except ValueError:
            self.log(f"{err} 时间格式不正确")
            return err

        # 校验时间条件
        if (sjdjsj_time > sjcssj_time) and (sjjjsj_time > sjdjsj_time):
            time_difference = sjdjsj_time - sjcssj_time
            if time_difference > datetime.timedelta(days=2):
                # self.log("校验失败：事件登记时间或事件解决时间未满足条件。")
                self.log(f"{proinstid} 事件登记时间比事件产生时间晚了超过2天")
                return err

            # else:
            # self.log(f"事件登记时间比事件产生时间晚了不超过2天 ({time_difference.days} 天)")

        # 检查处理过程及结果
        readme = ['己通知厂家 现场更换', '服务器硬件故障 更换备件设备恢复', '己更换光模块']
        if any(sjclgcjjg in entry for entry in readme):
            self.log(proinstid, '处理结果填写不规范')
            return err
        # self.log(f'{proinstid}  无问题')
        if self.work_close_time(proinstid):
            self.log(f"{proinstid}", xtsjgzdx.get('zt'), "未发现问题")
            return "所有校验通过"
        else:
            return err

    def main(self, monthly):
        result = self.work_list()
        work_running = self.work_running()

        def process_row(row):
            create_time = row.get('createTime')
            if not create_time:
                return None
            date_obj = datetime.datetime.strptime(create_time, '%Y-%m-%d %H:%M:%S')
            month = date_obj.month
            if int(month) == int(monthly):
                row_id = row['id']
                work_info = self.work_info(row_id)

                if work_info != '所有校验通过':
                    return row['id']

        # with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
        #     # 提交所有任务到线程池
        #     try:
        #         futures = [executor.submit(process_row, row) for row in result]
        #     except Exception as e:
        #         self.log(e)
        #     # 收集处理结果
        #     for future in concurrent.futures.as_completed(futures):
        #         subject = future.result()
        #         if subject:
        #             self.log(subject)
        for row in result:
            try:
                process_row(row)
            except Exception as e:
                self.log(f"{e}")
        for row in work_running:
            try:
                if not self.work_close_time(row['id']):
                    return False
            except Exception as e:
                self.log(f"{e}")
        self.log('事件跟踪单稽核结束')


def main():
    _console('请输入要稽核的月份，例如 8:')
    work_check = yun_ge(_console)
    work_check.main(month)

# if __name__ == '__main__':
#     _console =print
#     # month = input('请输入要稽核的月份，例如 8:')
#     month = 1
#     work_check = yun_ge(_console)
#     work_check.main(month)
